import os
import shutil
import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                            QLabel, QPushButton, QListWidget, QListWidgetItem, QTextEdit, 
                            QProgressBar, QFileDialog, QMessageBox, QDialog)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont


class FolderSelectionDialog(QDialog):
    """文件夹多选对话框（支持添加父文件夹及子文件夹）"""
    def __init__(self, parent=None, caption="选择文件夹", directory=""):
        super().__init__(parent)
        self.setWindowTitle(caption)
        self.resize(600, 400)
        
        self.selected_folders = []
        self.current_dir = directory
        
        # 创建布局
        layout = QVBoxLayout(self)
        
        # 列表显示已选文件夹
        self.list_view = QListWidget()
        layout.addWidget(self.list_view)
        
        # 按钮区
        btn_layout = QHBoxLayout()
        
        self.add_btn = QPushButton("添加单个文件夹")
        self.add_btn.clicked.connect(self.add_folder)
        btn_layout.addWidget(self.add_btn)
        
        self.add_parent_btn = QPushButton("添加父文件夹（包含子文件夹）")
        self.add_parent_btn.clicked.connect(self.add_parent_folder)
        btn_layout.addWidget(self.add_parent_btn)
        
        self.remove_btn = QPushButton("移除选中")
        self.remove_btn.clicked.connect(self.remove_folder)
        btn_layout.addWidget(self.remove_btn)
        
        self.ok_btn = QPushButton("确定")
        self.ok_btn.clicked.connect(self.accept)
        btn_layout.addWidget(self.ok_btn)
        
        self.cancel_btn = QPushButton("取消")
        self.cancel_btn.clicked.connect(self.reject)
        btn_layout.addWidget(self.cancel_btn)
        
        layout.addLayout(btn_layout)
    
    def add_folder(self):
        """添加单个文件夹"""
        folder = QFileDialog.getExistingDirectory(
            self, "选择单个文件夹", self.current_dir
        )
        if folder and folder not in self.selected_folders:
            self.selected_folders.append(folder)
            self.list_view.addItem(folder)
            self.current_dir = folder
    
    def add_parent_folder(self):
        """添加父文件夹，并自动导入其所有直接子文件夹"""
        parent_folder = QFileDialog.getExistingDirectory(
            self, "选择父文件夹", self.current_dir
        )
        if not parent_folder:
            return
        
        try:
            subfolders = []
            for entry in os.scandir(parent_folder):
                if entry.is_dir() and not entry.name.startswith('.'):
                    subfolders.append(entry.path)
            
            if not subfolders:
                QMessageBox.information(self, "提示", f"所选父文件夹下没有子文件夹")
                return
            
            added_count = 0
            for folder in subfolders:
                if folder not in self.selected_folders:
                    self.selected_folders.append(folder)
                    self.list_view.addItem(folder)
                    added_count += 1
            
            self.current_dir = parent_folder
            QMessageBox.information(self, "成功", f"已添加 {added_count} 个子文件夹（共 {len(subfolders)} 个）")
        
        except Exception as e:
            QMessageBox.critical(self, "错误", f"获取子文件夹失败：{str(e)}")
    
    def remove_folder(self):
        """移除选中的文件夹"""
        for item in self.list_view.selectedItems():
            row = self.list_view.row(item)
            del self.selected_folders[row]
            self.list_view.takeItem(row)
    
    def get_folders(self):
        """返回选中的文件夹列表"""
        return self.selected_folders


class CustomListWidget(QListWidget):
    """自定义列表控件，支持带删除按钮的列表项"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.item_list = []  # 存储实际路径（与列表项对应）

    def add_item_with_button(self, display_text, real_path, is_file):
        """添加带删除按钮的列表项"""
        # 创建自定义列表项
        item = QListWidgetItem()
        self.addItem(item)
        
        # 创建包含文本和按钮的widget
        widget = QWidget()
        layout = QHBoxLayout(widget)
        layout.setContentsMargins(5, 2, 5, 2)
        
        # 添加文本标签
        label = QLabel(display_text)
        label.setTextInteractionFlags(Qt.TextSelectableByMouse)  # 支持文本选中
        layout.addWidget(label, 1)  # 占大部分空间

        # 添加删除按钮（根据类型设置颜色及悬浮效果）
        delete_btn = QPushButton("×删除")
        delete_btn.setFixedSize(50, 50)

        # 基础样式 + 悬浮效果（包含缩放动画）
        if is_file:
            # 文件 - 黄色按钮（带悬浮效果）
            delete_btn.setStyleSheet("""
                QPushButton {
                    border-radius: 12px; 
                    font-weight: bold;
                    font-size: 16px;
                    background-color: #FFC107; 
                    color: black;
                    transition: transform 0.2s;  /* 平滑过渡动画 */
                }
                QPushButton:hover {
                    background-color: #FFA000;
                    color: white;
                    border: 2px solid #F57C00;
                    transform: scale(1.5);  /* 悬浮时放大1.5倍 */
                }
            """)
        else:
            # 文件夹 - 绿色按钮（带悬浮效果）
            delete_btn.setStyleSheet("""
                QPushButton {
                    border-radius: 12px; 
                    font-weight: bold;
                    font-size: 16px;
                    background-color: #4CAF50; 
                    color: white;
                    transition: transform 0.2s;  /* 平滑过渡动画 */
                }
                QPushButton:hover {
                    background-color: #388E3C;
                    color: white;
                    border: 2px solid #1B5E20;
                    transform: scale(1.5);  /* 悬浮时放大1.5倍 */
                }
            """)
        # 绑定删除事件（通过闭包传递当前索引）
        def create_delete_func(index):
            def delete_item():
                # 从列表中移除
                self.takeItem(index)
                # 从实际路径列表中移除
                del self.item_list[index]
                # 重新绑定后续按钮的索引
                self.rebind_buttons()
            return delete_item
        
        current_index = self.count() - 1
        delete_btn.clicked.connect(create_delete_func(current_index))
        layout.addWidget(delete_btn)
        
        # 设置widget为列表项的显示组件
        self.setItemWidget(item, widget)
        # 调整项高度
        item.setSizeHint(widget.sizeHint())
        # 保存实际路径
        self.item_list.append(real_path)

    def rebind_buttons(self):
        """重新绑定所有按钮的删除事件（解决索引偏移问题）"""
        for i in range(self.count()):
            item = self.item(i)
            widget = self.itemWidget(item)
            # 获取按钮（布局中的第二个元素）
            delete_btn = widget.findChild(QPushButton)
            # 移除旧连接
            try:
                delete_btn.disconnect()
            except:
                pass
            # 绑定新的删除事件
            def create_delete_func(index):
                def delete_item():
                    self.takeItem(index)
                    del self.item_list[index]
                    self.rebind_buttons()
                return delete_item
            delete_btn.clicked.connect(create_delete_func(i))

    def clear_all(self):
        """清空列表"""
        self.clear()
        self.item_list.clear()


class MultiFolderCopyGUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("多文件夹批量复制工具")
        self.resize(900, 650)
        self.setMinimumSize(800, 600)
        
        # 初始化界面
        self.init_ui()
        
        # 当前路径
        self.current_path = os.path.dirname(os.path.abspath(__file__))

    def init_ui(self):
        # 主Widget和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setContentsMargins(10, 10, 10, 10)
        main_layout.setSpacing(8)
        
        # -------------------------- 1. 待复制项选择区域 --------------------------
        source_label = QLabel("1. 选择需要复制的文件或文件夹:")
        source_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        main_layout.addWidget(source_label)
        
        # 选择按钮区
        source_btn_layout = QHBoxLayout()
        main_layout.addLayout(source_btn_layout)
        
        self.add_file_btn = QPushButton("添加文件...")
        self.add_file_btn.clicked.connect(self.add_files)
        source_btn_layout.addWidget(self.add_file_btn)
        
        self.add_folder_btn = QPushButton("添加文件夹...")
        self.add_folder_btn.clicked.connect(self.add_folders)
        source_btn_layout.addWidget(self.add_folder_btn)
        
        self.clear_source_btn = QPushButton("清除全部")
        self.clear_source_btn.clicked.connect(self.clear_all_items)
        source_btn_layout.addWidget(self.clear_source_btn)
        
        # 带删除按钮的待复制列表
        self.copy_list = CustomListWidget()
        self.copy_list.setAlternatingRowColors(True)
        main_layout.addWidget(self.copy_list)
        
        # 删除选中项按钮（保留批量删除功能）
        self.del_item_btn = QPushButton("删除选中项")
        self.del_item_btn.clicked.connect(self.remove_selected_items)
        main_layout.addWidget(self.del_item_btn)
        
        # -------------------------- 2. 目标文件夹选择区域 --------------------------
        target_label = QLabel("2. 选择目标文件夹 (可多选):")
        target_label.setFont(QFont("微软雅黑", 10, QFont.Bold))
        main_layout.addWidget(target_label)
        
        # 按钮区
        target_btn_layout = QHBoxLayout()
        main_layout.addLayout(target_btn_layout)
        
        self.add_target_btn = QPushButton("添加目标文件夹...")
        self.add_target_btn.clicked.connect(self.add_target_folders)
        target_btn_layout.addWidget(self.add_target_btn)
        
        self.clear_target_btn = QPushButton("清除全部")
        self.clear_target_btn.clicked.connect(self.clear_target_folders)
        target_btn_layout.addWidget(self.clear_target_btn)
        
        # 带删除按钮的目标文件夹列表
        self.target_list = CustomListWidget()
        self.target_list.setAlternatingRowColors(True)
        main_layout.addWidget(self.target_list)
        
        # 删除选中目标文件夹按钮（保留批量删除功能）
        self.del_target_btn = QPushButton("删除选中目标文件夹")
        self.del_target_btn.clicked.connect(self.remove_selected_targets)
        main_layout.addWidget(self.del_target_btn)
        
        # -------------------------- 操作按钮区 --------------------------
        op_btn_layout = QHBoxLayout()
        main_layout.addLayout(op_btn_layout)
        
        self.start_btn = QPushButton("开始复制")
        self.start_btn.setFont(QFont("微软雅黑", 9, QFont.Bold))
        self.start_btn.setStyleSheet("background-color: #4CAF50; color: white;")
        self.start_btn.clicked.connect(self.start_copy)
        op_btn_layout.addWidget(self.start_btn)
        
        self.clear_log_btn = QPushButton("清除日志")
        self.clear_log_btn.clicked.connect(self.clear_log)
        op_btn_layout.addWidget(self.clear_log_btn)
        
        # -------------------------- 进度条 --------------------------
        progress_label = QLabel("复制进度:")
        main_layout.addWidget(progress_label)
        
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        main_layout.addWidget(self.progress_bar)
        
        # -------------------------- 日志区域 --------------------------
        log_label = QLabel("操作日志:")
        main_layout.addWidget(log_label)
        
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        self.log_text.setMinimumHeight(100)
        main_layout.addWidget(self.log_text)

    # -------------------------- 待复制项操作 --------------------------
    def add_files(self):
        """添加文件到待复制列表"""
        files, _ = QFileDialog.getOpenFileNames(
            self, "选择要复制的文件", self.current_path, "所有文件 (*)"
        )
        if files:
            for file in files:
                # 避免重复添加
                if file not in self.copy_list.item_list:
                    # 显示文本：[文件] + 路径
                    display_text = f"[文件] {file}"
                    self.copy_list.add_item_with_button(display_text, file, is_file=True)
            self.current_path = os.path.dirname(files[0])

    def add_folders(self):
        """添加文件夹到待复制列表"""
        dialog = FolderSelectionDialog(
            self, "选择要复制的文件夹", self.current_path
        )
        if dialog.exec_():
            folders = dialog.get_folders()
            if folders:
                for folder in folders:
                    if folder not in self.copy_list.item_list:
                        # 显示文本：[文件夹] + 路径
                        display_text = f"[文件夹] {folder}"
                        self.copy_list.add_item_with_button(display_text, folder, is_file=False)
                self.current_path = os.path.dirname(folders[0])

    def clear_all_items(self):
        """清除所有待复制项"""
        self.copy_list.clear_all()

    def remove_selected_items(self):
        """删除选中的待复制项（批量删除）"""
        # 从后往前删，避免索引问题
        for item in reversed(self.copy_list.selectedItems()):
            row = self.copy_list.row(item)
            self.copy_list.takeItem(row)
            del self.copy_list.item_list[row]
        # 重新绑定按钮事件
        self.copy_list.rebind_buttons()

    # -------------------------- 目标文件夹操作 --------------------------
    def add_target_folders(self):
        """添加目标文件夹"""
        dialog = FolderSelectionDialog(
            self, "选择目标文件夹", self.current_path
        )
        if dialog.exec_():
            folders = dialog.get_folders()
            if folders:
                for folder in folders:
                    if folder not in self.target_list.item_list:
                        self.target_list.add_item_with_button(folder, folder, is_file=False)
                self.current_path = os.path.dirname(folders[0])

    def clear_target_folders(self):
        """清除所有目标文件夹"""
        self.target_list.clear_all()

    def remove_selected_targets(self):
        """删除选中的目标文件夹（批量删除）"""
        for item in reversed(self.target_list.selectedItems()):
            row = self.target_list.row(item)
            self.target_list.takeItem(row)
            del self.target_list.item_list[row]
        self.target_list.rebind_buttons()

    # -------------------------- 日志和进度条 --------------------------
    def log(self, message):
        self.log_text.append(message)
        self.log_text.moveCursor(self.log_text.textCursor().End)

    def clear_log(self):
        self.log_text.clear()

    # -------------------------- 核心复制功能 --------------------------
    def start_copy(self):
        """开始复制操作"""
        items_to_copy = self.copy_list.item_list
        target_folders = self.target_list.item_list
        
        # 验证输入
        if not items_to_copy:
            QMessageBox.critical(self, "错误", "请至少选择一个文件或文件夹进行复制！")
            return
        if not target_folders:
            QMessageBox.critical(self, "错误", "请至少选择一个目标文件夹！")
            return

        total_items = len(items_to_copy)
        total_targets = len(target_folders)
        total_operations = total_items * total_targets
        completed = 0

        self.log("===== 开始复制操作 =====")
        self.log(f"待复制项总数: {total_items} 个")
        self.log(f"目标文件夹: {total_targets} 个")

        # 遍历所有目标文件夹
        for target_idx, target_folder in enumerate(target_folders, 1):
            self.log(f"\n处理目标文件夹 {target_idx}/{total_targets}: {target_folder}")
            
            # 遍历所有待复制项
            for item_idx, item in enumerate(items_to_copy, 1):
                item_name = os.path.basename(item)
                target_path = os.path.join(target_folder, item_name)

                try:
                    os.makedirs(os.path.dirname(target_path), exist_ok=True)

                    if os.path.isfile(item):
                        shutil.copy2(item, target_path)
                        self.log(f"  文件 {item_idx}/{total_items}: {item_name} [复制成功]")
                    elif os.path.isdir(item):
                        if not os.path.exists(target_path):
                            shutil.copytree(item, target_path)
                            self.log(f"  文件夹 {item_idx}/{total_items}: {item_name} [复制成功]")
                        else:
                            self.log(f"  文件夹 {item_idx}/{total_items}: {item_name} [已存在，跳过]")
                    else:
                        self.log(f"  未知类型 {item_idx}/{total_items}: {item_name} [跳过]")

                except Exception as e:
                    self.log(f"  错误 {item_idx}/{total_items}: {item_name} → {str(e)}")

                # 更新进度
                completed += 1
                progress = int((completed / total_operations) * 100)
                self.progress_bar.setValue(progress)
                QApplication.processEvents()

        self.log("\n===== 操作完成 =====")
        QMessageBox.information(self, "完成", "所有复制操作已完成！")
        self.progress_bar.setValue(100)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    font = QFont("SimHei")
    app.setFont(font)
    window = MultiFolderCopyGUI()
    window.show()
    sys.exit(app.exec_())